#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "bmp.h"

extern int *lcd_ptr;

#pragma pack(1) // 设置1字节对齐模式，pack()将对齐模式取消
/*位图文件头*/
typedef struct BMP_FILE_HEADER
{
	uint16_t bType;		 /*  文件标识符          */
	uint32_t bfSize;	 /*  文件的大小，单位字节 */
	uint16_t bReserved1; /*  保留值,必须设置为0  */
	uint16_t bReserved2; /*  保留值,必须设置为0  */
	uint32_t bOffset;	 /*  文件头的最后到图像数据位开始的偏移量    */
} BMPFILEHEADER;		 // 14字节

/*位图信息头*/
typedef struct BMP_INFO
{
	uint32_t bInfoSize;		 /*  信息头的大小             */
	uint32_t bWidth;		 /*  图像的宽度               */
	uint32_t bHeight;		 /*  图像的高度               */
	uint16_t bPlanes;		 /*  图像的位面数             */
	uint16_t bBitCount;		 /*  每个像素的位数           */
	uint32_t bCompression;	 /*  压缩类型                 */
	uint32_t bmpImageSize;	 /*  图像的大小,以字节为单位   */
	uint32_t bXPelsPerMeter; /*  水平分辨率               */
	uint32_t bYPelsPerMeter; /*  垂直分辨率               */
	uint32_t bClrUsed;		 /*  使用的色彩数             */
	uint32_t bClrImportant;	 /*  重要的颜色数             */
} BMPINF;					 // 40字节
#pragma pack()

/*彩色表*/
typedef struct RGB_QUAD
{
	uint16_t rgbBlue;	  /*  蓝色强度  */
	uint16_t rgbGreen;	  /*  绿色强度  */
	uint16_t rgbRed;	  /*  红色强度  */
	uint16_t rgbReversed; /*  保留值    */
} RGBQUAD;

int lcd_draw_point(int i, int j, int color)
{
	*(lcd_ptr + 800 * j + i) = color;
	return 0;
}

int lcd_draw_bmp(const char *pathname, int x, int y)
{
	int i, j;

	// 打开图片文件
	int bmp_fd = open(pathname, O_RDWR);

	// 错误处理
	if (bmp_fd == -1)
	{
		printf("open bmp file failed!\n");
		return -1;
	}

	BMPFILEHEADER file_head; // 位图文件头
	BMPINF info_head;		 // 位图信息头

	// 读取图片信息头，得到图片的相关信息
	read(bmp_fd, &file_head, sizeof(file_head));
	read(bmp_fd, &info_head, sizeof(info_head));

	int w = info_head.bWidth;
	int h = info_head.bHeight;

	char rgb_buf[w * h * 3];
	
	// 将图片颜色数据读取出来
	int pad = (4 - (w * 3) % 4) % 4;

	for (i = 0; i < h; i++)
	{
		// 读取图片颜色数据
		read(bmp_fd, &rgb_buf[w * i * 3], w * 3);
		// 跳过无效字节
		lseek(bmp_fd, pad, SEEK_CUR);
	}

	// 加载数据到lcd屏幕
	for (j = 0; j < h; j++)
	{
		for (i = 0; i < w; i++)
		{
			int b = rgb_buf[(j * w + i) * 3 + 0];
			int g = rgb_buf[(j * w + i) * 3 + 1];
			int r = rgb_buf[(j * w + i) * 3 + 2];

			int color = b;
			color |= (g << 8);
			color |= (r << 16);

			lcd_draw_point(i + x, h - 1 - j + y, color);
		}
	}

	// 关闭文件
	close(bmp_fd);
	return 0;
}

int lcd_draw_scale_bmp(const char *pathname, int x, int y, int w, int h)
{
	int i, j;

	// 打开图片文件
	int bmp_fd = open(pathname, O_RDWR);

	// 错误处理
	if (bmp_fd == -1)
	{
		printf("open bmp file failed!\n");
		return -1;
	}

	// 读取图片头信息
	char header[54];
	read(bmp_fd, header, 54);

	// 获取 bmp 图片的宽度，高度，大小
	int bmp_w, bmp_h, bmp_size;
	memcpy(&bmp_w, &header[18], sizeof(int));
	memcpy(&bmp_h, &header[22], sizeof(int));
	memcpy(&bmp_size, &header[34], sizeof(int));

	// 计算宽度缩放比例和高度缩放比例
	int scale_w = bmp_w / w;
	int scale_h = bmp_h / h;

	// 分配存储bmp图片数据的内存空间
	char *bmp_buf = (char *)calloc(1, bmp_size);
	// 分配存储缩放后图片数据的内存空间
	char *scale_buf = (char *)calloc(1, w * h * 3);

	// 读取 bmp 图片数据
	int pad = (4 - (bmp_w * 3) % 4) % 4; // 计算每一行的无效字节数

	for (i = 0; i < bmp_h; i++)
	{
		// 读取图片颜色数据
		read(bmp_fd, &bmp_buf[bmp_w * i * 3], bmp_w * 3);
		// 跳过无效字节
		lseek(bmp_fd, pad, SEEK_CUR);
	}

	// 对缩放后的每一个像素点进行计算
	for (j = 0; j < h; j++)
	{
		for (i = 0; i < w; i++)
		{
			int k, l;
			int b = 0, g = 0, r = 0;
			for (k = 0; k < scale_h; k++)
			{
				for (l = 0; l < scale_w; l++)
				{
					b += bmp_buf[((bmp_h - 1 - j * scale_h - k) * bmp_w + i * scale_w + l) * 3 + 0];
					g += bmp_buf[((bmp_h - 1 - j * scale_h - k) * bmp_w + i * scale_w + l) * 3 + 1];
					r += bmp_buf[((bmp_h - 1 - j * scale_h - k) * bmp_w + i * scale_w + l) * 3 + 2];
				}
			}
			b /= (scale_w * scale_h);
			g /= (scale_w * scale_h);
			r /= (scale_w * scale_h);

			scale_buf[(j * w + i) * 3 + 0] = b;
			scale_buf[(j * w + i) * 3 + 1] = g;
			scale_buf[(j * w + i) * 3 + 2] = r;
		}
	}

	// 加载数据到lcd屏幕
	for (j = 0; j < h; j++)
	{
		for (i = 0; i < w; i++)
		{
			int b = scale_buf[(j * w + i) * 3 + 0];
			int g = scale_buf[(j * w + i) * 3 + 1];
			int r = scale_buf[(j * w + i) * 3 + 2];

			int color = b;
			color |= (g << 8);
			color |= (r << 16);

			lcd_draw_point(i + x, j + y, color);
		}
	}

	// 释放资源
	free(bmp_buf);
	free(scale_buf);
	// 关闭图片文件
	close(bmp_fd);

	return 0;
} 